-module(storager).
-behaviour(gen_server).

% gen_server API
-export([start_link/0]).

% gen_server API
-export([init/1, handle_call/3,handle_cast/2,handle_info/2
        ,terminate/2, code_change/3]).


start_link() ->
    io:format("Starting storager\n"),
    gen_server:start_link({local,storager},storager,[],[]).

% Initial dictionary
% should return {ok, State}
init(Dict) ->
    {ok, Dict}.

%
% Handle put
% This implementation uses 
%
handle_call({put, Key, Value}, _From, Values) ->
    NewValues = orddict:store(hash:hash(Key), Value, Values),
    io:format("Inserting: (~p,~p)~n",[Key,Value]),
    delay(5),
    {reply,{ok,put}, NewValues};
%
% Handle get
%
handle_call({get, Key}, _From, Values) ->
    Reply = orddict:find(hash:hash(Key), Values),
    {reply, Reply, Values};

%
% Split the current values between this node and 
% the joining node.
% TODO working correcly?
handle_call({newJoin, Node}, _From, Dict) ->
    ToList = orddict:to_list(Dict),
    %{ReplyList, KeepList} = lists:splitwith(fun({Key, _Value}) 
    %                                    -> (Key =< hash:hash(Node)) or
    %                                       (Key >  hash:hash(node())) end
    %                                  , ToList),
    %                                  
    % Temporary solution
    TmpNodeSet = ordsets:add_element({hash:hash(node()),node()}
           ,ordsets:add_element({hash:hash(Node),Node}, ordsets:new())),
    ReplyList = [{Hash,N} || {Hash,N} <- ToList
                , Node == chorder:find_responsible_node(N,TmpNodeSet)],
    KeepList = lists:subtract(ToList,ReplyList),
    %
    ReplyDict = orddict:from_list(ReplyList),
    NewDict = orddict:from_list(KeepList),
    io:format("Values: ~p~n",[NewDict]),
    {reply, ReplyDict, NewDict};

%
% Store values received when new chord network is joined
%
handle_call({newValues, NewValues}, _From, _Values) ->
    io:format("Values: ~p~n",[NewValues]),
    {reply, {ok, newValues}, NewValues};

%
% This node want to leave chord network
%
handle_call({leave}, _From, Values) ->
    {reply, Values, orddict:new()};

%
% Merge values from the leaving node
%
handle_call({leavingNode, LeaveDict}, _From, Dict) ->
    NewDict = orddict:merge(fun(_K, V1, _V2) -> V1 end, Dict, LeaveDict),
    io:format("Values: ~p~n",[NewDict]),
    {reply, {ok, leave}, NewDict};

% status
handle_call({status}, _From, Values) ->
    {reply, Values, Values}.


terminate(_Reason,_State) ->
    io:format("Terminating storager~n").

% Unused functions atm
handle_cast(_Request,_State) ->
    undefined.

handle_info(_Info, _State) ->
    undefined.

code_change(_OldVsn, _State, _Extra) ->
    undefined.

delay(Time) ->
   receive
   after Time -> ok
   end.
